using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using EmitMapper.MappingConfiguration;
using EmitMapper.MappingConfiguration.MappingOperations;
using Extendsoft.HMIS.DataSync.FileProcess;

namespace Extendsoft.HMIS.DataSync.Mapping.Configurators
{
    /// <summary>
    /// The item configuration.
    /// </summary>
    public class EntityToDataContainerPropertyMappingConfigurator : DefaultMapConfig
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EntityToDataContainerPropertyMappingConfigurator"/> class.
        /// </summary>
        public EntityToDataContainerPropertyMappingConfigurator()
        {
            ConstructBy(() => new DataContainer { Fields = new Dictionary<string, string>() });
        }

        /// <summary>
        /// Gets the mapping operations.
        /// </summary>
        /// <param name="from">The type from.</param>
        /// <param name="to">To type to.</param>
        /// <returns>The mapping operations.</returns>
        public override IMappingOperation[] GetMappingOperations(Type from, Type to)
        {
            return this.FilterOperations(from, to, EmitMapper.Utils.ReflectionUtils.GetPublicFieldsAndProperties(from)
                                    .Where(member => (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property) && ((PropertyInfo)member).GetGetMethod() != null)
                                    .Select(sourceMember => (IMappingOperation)new SrcReadOperation
                                    {
                                        Source = new MemberDescriptor(sourceMember),
                                        Setter = (destination, value, state) =>
                                        {
                                            if (destination == null || value == null || !(destination is DataContainer))
                                            {
                                                return;
                                            }

                                            var sourceType = EmitMapper.Utils.ReflectionUtils.GetMemberType(sourceMember);
                                            var fieldsDescription = ReflectionUtils.GetDataMemberDefinition(sourceMember);
                                            ConvertSourcePropertyToFields(value, sourceType, (DataContainer)destination, (List<Tuple<string, Type>>)fieldsDescription);
                                        }
                                    })).ToArray();
        }

        /// <summary>
        /// Converts the source property to fields.
        /// </summary>
        /// <param name="sourceType">Type of the property.</param>
        /// <param name="sourceValue">The property value.</param>
        /// <param name="container">The container.</param>
        /// <param name="fieldsDescription">The fields description.</param>
        private static void ConvertSourcePropertyToFields(object sourceValue, Type sourceType, DataContainer container, List<Tuple<string, Type>> fieldsDescription)
        {
            if (container == null || container.Fields == null)
            {
                return;
            }

            fieldsDescription.ForEach(fd =>
            {
                if (container.Fields.ContainsKey(fd.Item1))
                {
                    return;
                }

                var value = ReflectionUtils.ConvertValue(sourceValue, sourceType, fd.Item2);

                if (value != null)
                {
                    container.Fields.Add(fd.Item1, value.ToString());
                }
            });
        }
    }
}
